创建时间: | 2016/6/29 15:19 |
更新时间: | 2016/6/29 15:20 |
原始类型:值传递
string
number
boolean
null
undefined
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
复杂类型:引用传递
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
为引用使用 const
关键字,而不是 var
这样确保你不能修改引用类型,否则可能会导致一些 bug 或难以理解的代码。
// bad var a = 1; var b = 2; // good const a = 1; const b = 2;
如果你必须修改引用,使用 let
代替 var
因为 let
是块作用域的,而 var
是函数作用域。
// bad var count = 1; if (true) { count += 1; } // good, use the let. let count = 1; if (true) { count += 1; }
let
和 const
都是块作用域的// const and let only exist in the blocks they are defined in. { let a = 1; const b = 1; } console.log(a); // ReferenceError console.log(b); // ReferenceError
// bad var item = new Object(); // good var item = {};
// bad var superman = { class: 'superhero', default: { clark: 'kent' }, private: true }; // good var superman = { klass: 'superhero', defaults: { clark: 'kent' }, hidden: true };
// bad const superman = { class: 'alien' }; // bad const superman = { klass: 'alien' }; // good const superman = { type: 'alien' };
创建对象时使用计算的属性名,而不要在创建对象后使用对象的动态特性
这样可以在同一个位置定义对象的所有属性。
function getKey(k) { return `a key named ${k}`; } // bad const obj = { id: 5, name: 'San Francisco' }; obj[getKey('enabled')] = true; // good const obj = { id: 5, name: 'San Francisco', [getKey('enabled')]: true };
// bad const atom = { value: 1, addValue: function (value) { return atom.value + value; } }; // good const atom = { value: 1, addValue(value) { return atom.value + value; } };
使用定义对象属性的简短形式
书写起来更加简单,并且可以自描述。
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker }; // good const obj = { lukeSkywalker };
将所有简写的属性写在对象定义的最顶部
这样可以更加方便地知道哪些属性使用了简短形式。
const anakinSkywalker = 'Anakin Skywalker'; const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { episodeOne: 1, twoJedisWalkIntoACantina: 2, lukeSkywalker, episodeThree: 3, mayTheFourth: 4, anakinSkywalker }; // good const obj = { lukeSkywalker, anakinSkywalker, episodeOne: 1, twoJedisWalkIntoACantina: 2, episodeThree: 3, mayTheFourth: 4 };
// bad const items = new Array(); // good const items = [];
push
const someStack = []; // bad someStack[someStack.length] = 'abracadabra'; // good someStack.push('abracadabra');
...
来拷贝数组// bad const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // good const itemsCopy = [...items];
Array.from
将类数组对象转换为数组const foo = document.querySelectorAll('.foo'); const nodes = Array.from(foo);
访问或使用对象的多个属性时请使用对象的解构赋值
解构赋值避免了为这些属性创建临时变量或对象。
// bad function getFullName(user) { const firstName = user.firstName; const lastName = user.lastName; return `${firstName} ${lastName}`; } // good function getFullName(obj) { const { firstName, lastName } = obj; return `${firstName} ${lastName}`; } // best function getFullName({ firstName, lastName }) { return `${firstName} ${lastName}`; }
const arr = [1, 2, 3, 4]; // bad const first = arr[0]; const second = arr[1]; // good const [first, second] = arr;
函数有多个返回值时使用对象解构,而不是数组解构
这样你就可以随时添加新的返回值或任意改变返回值的顺序,而不会导致调用失败。
function processInput(input) { // then a miracle occurs return [left, right, top, bottom]; } // the caller needs to think about the order of return data const [left, __, top] = processInput(input); // good function processInput(input) { // then a miracle occurs return { left, right, top, bottom }; } // the caller selects only the data they need const { left, right } = processInput(input);
''
// bad var name = "Bob Parr"; // good var name = 'Bob Parr'; // bad var fullName = "Bob " + this.lastName; // good var fullName = 'Bob ' + this.lastName;
// bad var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; // bad var errorMessage = 'This is a super long error that \ was thrown because of Batman. \ When you stop to think about \ how Batman had anything to do \ with this, you would get nowhere \ fast.'; // good var errorMessage = 'This is a super long error that ' + 'was thrown because of Batman.' + 'When you stop to think about ' + 'how Batman had anything to do ' + 'with this, you would get nowhere ' + 'fast.';
编程构建字符串时,使用字符串模板而不是字符串连接
模板给你一个可读的字符串,简洁的语法与适当的换行和字符串插值特性。
// bad function sayHi(name) { return 'How are you, ' + name + '?'; } // bad function sayHi(name) { return ['How are you, ', name, '?'].join(); } // good function sayHi(name) { return `How are you, ${name}?`; }
使用函数声明而不是函数表达式
函数声明拥有函数名,在调用栈中更加容易识别。并且,函数声明会整体提升,而函数表达式只会提升变量本身。这条规则也可以这样描述,始终使用 箭头函数 来代替函数表达式。
// bad const foo = function () { }; // good function foo() { }
// immediately-invoked function expression (IIFE) (() => { console.log('Welcome to the Internet. Please follow me.'); })();
块
定义为一组语句,函数声明不是一个语句。阅读 ECMA-262 对这个问题的说明// bad if (currentUser) { function test() { console.log('Nope.'); } } // good if (currentUser) { var test = function test() { console.log('Yup.'); }; }
arguments
, 这将会覆盖函数作用域内传过来的arguments
对象// bad function nope(name, options, arguments) { // ...stuff... } // good function yup(name, options, args) { // ...stuff... }
永远不要使用 arguments
,使用 ...
操作符来代替
...
操作符可以明确指定你需要哪些参数,并且得到的是一个真实的数组,而不是 arguments
这样的类数组对象。
// bad function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // good function concatenateAll(...args) { return args.join(''); }
// really bad function handleThings(opts) { // No! We shouldn't mutate function arguments. // Double bad: if opts is falsy it'll be set to an object which may // be what you want but it can introduce subtle bugs. opts = opts || {}; // ... } // still bad function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // good function handleThings(opts = {}) { // ... }
当必须使用函数表达式时(例如传递一个匿名函数时),请使用箭头函数
箭头函数提供了更简洁的语法,并且箭头函数中 this
对象的指向是不变的,this
对象绑定定义时所在的对象,这通常是我们想要的。如果该函数的逻辑非常复杂,请将该函数提取为一个函数声明。
// bad [1, 2, 3].map(function (x) { return x * x; }); // good [1, 2, 3].map((x) => { return x * x });
// bad [1, 2, 3].map(x => x * x); // good [1, 2, 3].map((x) => x * x);
总是使用 class
关键字,避免直接修改 prototype
class
语法更简洁,也更易理解。
// bad function Queue(contents = []) { this._queue = [...contents]; } Queue.prototype.pop = function() { const value = this._queue[0]; this._queue.splice(0, 1); return value; } // good class Queue { constructor(contents = []) { this._queue = [...contents]; } pop() { const value = this._queue[0]; this._queue.splice(0, 1); return value; } }
使用 extends
关键字来继承
这是一个内置的继承方式,并且不会破坏 instanceof
原型检查。
// bad const inherits = require('inherits'); function PeekableQueue(contents) { Queue.apply(this, contents); } inherits(PeekableQueue, Queue); PeekableQueue.prototype.peek = function() { return this._queue[0]; } // good class PeekableQueue extends Queue { peek() { return this._queue[0]; } }
this
以方便链式调用// bad Jedi.prototype.jump = function() { this.jumping = true; return true; }; Jedi.prototype.setHeight = function(height) { this.height = height; }; const luke = new Jedi(); luke.jump(); // => true luke.setHeight(20); // => undefined // good class Jedi { jump() { this.jumping = true; return this; } setHeight(height) { this.height = height; return this; } } const luke = new Jedi(); luke.jump() .setHeight(20);
class Jedi { contructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } }
总是在非标准的模块系统中使用标准的 import
和 export
语法,我们总是可以将标准的模块语法转换成支持特定模块加载器的语法。
模块是未来的趋势,那么我们为何不现在就开始使用。
// bad const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // ok import AirbnbStyleGuide from './AirbnbStyleGuide'; export default AirbnbStyleGuide.es6; // best import { es6 } from './AirbnbStyleGuide'; export default es6;
不要使用通配符 *
的 import
这样确保了只有一个默认的 export
项
// bad import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // good import AirbnbStyleGuide from './AirbnbStyleGuide';
不要直接从一个 import
上 export
虽然一行代码看起来更简洁,但是有一个明确的 import
和一个明确的 export
使得代码行为更加明确。
// bad // filename es6.js export default { es6 } from './airbnbStyleGuide'; // good // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;
不要使用迭代器(Iterators)。优先使用 JavaScript 中 map
和 reduce
这类高阶函数来代替 for-of
循环
处理纯函数的返回值更加容易并且没有副作用
const numbers = [1, 2, 3, 4, 5]; // bad let sum = 0; for (let num of numbers) { sum += num; } sum === 15; // good let sum = 0; numbers.forEach((num) => sum += num); sum === 15; // best (use the functional force) const sum = numbers.reduce((total, num) => total + num, 0); sum === 15;
不要使用 generators
它们不太容易转换为 ES5 的语法。
.
操作符来访问属性const luke = { jedi: true, age: 28 }; // bad const isJedi = luke['jedi']; // good const isJedi = luke.jedi;
[]
var luke = { jedi: true, age: 28 }; function getProp(prop) { return luke[prop]; } var isJedi = getProp('jedi');
const
来声明变量,否则将生成全局变量,我们应该避免污染全局命名空间// bad superPower = new SuperPower(); // good const superPower = new SuperPower();
为每个变量都使用 const
关键字声明
这种方式更加容易添加新变量,并且不必担忧将 ,
错误写成 ;
而导致生成全局变量。
// bad const items = getItems(), goSportsTeam = true, dragonball = 'z'; // bad // (compare to above, and try to spot the mistake) const items = getItems(), goSportsTeam = true; dragonball = 'z'; // good const items = getItems(); const goSportsTeam = true; const dragonball = 'z';
const
变量放在一起,然后将所有 let
变量放在一起// bad let i, len, dragonball, items = getItems(), goSportsTeam = true; // bad let i; let items = getItems(); let dragonball; let goSportsTeam = true; let len; // good const goSportsTeam = true; const items = getItems(); let dragonball; let i; let length;
在必要的时候声明变量,并且将其放在合适的位置
let
和 const
是块级作用域的,而不是函数作用域。
// good function() { test(); console.log('doing stuff..'); //..other stuff.. const name = getName(); if (name === 'test') { return false; } return name; } // bad function() { const name = getName(); if (!arguments.length) { return false; } return true; } // good function() { if (!arguments.length) { return false; } const name = getName(); return true; }
var
声明的变量将被提升到作用域的顶部,但他们的赋值不会被提升。通过const
和 let
声明的变量不存在变量提升,这里有一个新概念,称为“ 暂时性死区( Temporal Dead Zones (TDZ)) ”。有必要理解 typeof
不再是一个百分之百安全的操作 。// we know this wouldn't work (assuming there // is no notDefined global variable) function example() { console.log(notDefined); // => throws a ReferenceError } // creating a variable declaration after you // reference the variable will work due to // variable hoisting. Note: the assignment // value of `true` is not hoisted. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; } // The interpreter is hoisting the variable // declaration to the top of the scope, // which means our example could be rewritten as: function example() { let declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; } // using const and let function example() { console.log(declaredButNotAssigned); // => throws a ReferenceError console.log(typeof declaredButNotAssigned); // => throws a ReferenceError const declaredButNotAssigned = true; }
function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous is not a function let anonymous = function() { console.log('anonymous function expression'); }; }
function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } // the same is true when the function name // is the same as the variable name. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); } }
function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
===
和 !==
而不是 ==
和 !=
比较运算通过 ToBoolean
强制转换并遵循一下规则:
Object
- true
Undefined
- false
Null
- false
Booleans
- 被转换为对应的值Number
- 值为 +0
, -0
, NaN
时为 false
,否则为 true
String
- 空字符串 ''
为 false
,否则为 true
if ([0]) { // true // An array is an object, objects evaluate to true }
// bad if (name !== '') { // ...stuff... } // good if (name) { // ...stuff... } // bad if (collection.length > 0) { // ...stuff... } // good if (collection.length) { // ...stuff... }
// bad if (test) return false; // good if (test) return false; // good if (test) { return false; } // bad function() { return false; } // good function() { return false; }
if...else
这样的多行块时,请将 else
和 if
的结束括号放在同一行// bad if (test) { thing1(); thing2(); } else { thing3(); } // good if (test) { thing1(); thing2(); } else { thing3(); }
/** ... */
进行多行注释,包括描述,指定类型以及参数值和返回值// bad // make() returns a new element // based on the passed in tag name // // @param <String> tag // @return <Element> element function make(tag) { // ...stuff... return element; } // good /** * make() returns a new element * based on the passed in tag name * * @param <String> tag * @return <Element> element */ function make(tag) { // ...stuff... return element; }
//
进行单行注释,将注释放在被注释对象的上面,并在注释之前保留一个空行// bad const active = true; // is current tab // good // is current tab const active = true; // bad function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this._type || 'no type'; return type; } // good function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this._type || 'no type'; return type; }
// FIXME:
来注释一个问题function Calculator() { // FIXME: shouldn't use a global here total = 0; return this; }
// TODO:
来注释一个问题的解决方案function Calculator() { // TODO: total should be configurable by an options param this.total = 0; return this; }
tab
设置为 2
个空格缩进// bad function() { ∙∙∙∙const name; } // bad function() { ∙const name; } // good function() { ∙∙const name; }
// bad function test(){ console.log('test'); } // good function test() { console.log('test'); } // bad dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog' }); // good dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog' });
// bad const x=y+5; // good const x = y + 5;
// bad (function(global) { // ...stuff... })(this);
// bad (function(global) { // ...stuff... })(this);↵ ↵
// good (function(global) { // ...stuff... })(this);↵
.
来表示该行是一个方法调用,而不是一个新语句// bad $('#items').find('.selected').highlight().end().find('.open').updateCount(); // bad $('#items'). find('selected'). highlight(). end(). find('.open'). updateCount(); // good $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount(); // bad const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true) .attr('width', (radius + margin) * 2).append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led); // good const leds = stage.selectAll('.led') .data(data) .enter().append('svg:svg') .class('led', true) .attr('width', (radius + margin) * 2) .append('svg:g') .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')') .call(tron.led);
// bad if (foo) { return bar; } return baz; // good if (foo) { return bar; } return baz; // bad const obj = { foo: function() { }, bar: function() { } }; return obj; // good const obj = { foo: function() { }, bar: function() { } }; return obj;
// bad const story = [ once , upon , aTime ]; // good const story = [ once, upon, aTime ]; // bad const hero = { firstName: 'Bob' , lastName: 'Parr' , heroName: 'Mr. Incredible' , superPower: 'strength' }; // good const hero = { firstName: 'Bob', lastName: 'Parr', heroName: 'Mr. Incredible', superPower: 'strength' };
// bad const hero = { firstName: 'Kevin', lastName: 'Flynn', }; const heroes = [ 'Batman', 'Superman', ]; // good const hero = { firstName: 'Kevin', lastName: 'Flynn' }; const heroes = [ 'Batman', 'Superman' ];
// bad (function() { const name = 'Skywalker' return name })() // good (() => { const name = 'Skywalker'; return name; })(); // good (guards against the function becoming an argument when two files with IIFEs are concatenated) ;(() => { const name = 'Skywalker'; return name; })();
// => this.reviewScore = 9; // bad const totalScore = this.reviewScore + ''; // good const totalScore = String(this.reviewScore);
const inputValue = '4'; // bad const val = new Number(inputValue); // bad const val = +inputValue; // bad const val = inputValue >> 0; // bad const val = parseInt(inputValue); // good const val = Number(inputValue); // good const val = parseInt(inputValue, 10);
parseInt
是一个瓶颈而需要位运算来解决某些 性能问题 ,请为你的代码注释为什么要这样做// good /** * parseInt was the reason my code was slow. * Bitshifting the String to coerce it to a * Number made it a lot faster. */ const val = inputValue >> 0;
Number
在 JavaScript 中表示为 64 位的值,但位移运算总是返回一个 32 位的整数( source ),对大于 32 位的整数进行位移运算会导致意外的结果( 讨论 )。32 位最大整数为 2,147,483,647
:2147483647 >> 0 //=> 2147483647 2147483648 >> 0 //=> -2147483648 2147483649 >> 0 //=> -2147483647
var age = 0; // bad var hasAge = new Boolean(age); // good var hasAge = Boolean(age); // good var hasAge = !!age;
// bad function q() { // ...stuff... } // good function query() { // ..stuff.. }
// bad var OBJEcttsssss = {}; var this_is_my_object = {}; var this-is-my-object = {}; function c() {}; var u = new user({ name: 'Bob Parr' }); // good var thisIsMyObject = {}; function thisIsMyFunction() {}; var user = new User({ name: 'Bob Parr' });
// bad function user(options) { this.name = options.name; } const bad = new user({ name: 'nope' }); // good class User { constructor(options) { this.name = options.name; } } const good = new User({ name: 'yup' });
_
// bad this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; // good this._firstName = 'Panda';
this
的引用时使用 _this
// bad function() { var self = this; return function() { console.log(self); }; } // bad function() { var that = this; return function() { console.log(that); }; } // good function() { var _this = this; return function() { console.log(_this); }; }
// file contents class CheckBox { // ... } module.exports = CheckBox; // in some other file // bad const CheckBox = require('./checkBox'); // bad const CheckBox = require('./check_box'); // good const CheckBox = require('./CheckBox');
function makeStyleGuide() { } export default makeStyleGuide;
const AirbnbStyleGuide = { es6: { } }; export default AirbnbStyleGuide;
getVal()
和 setVal('hello')
// bad dragon.age(); // good dragon.getAge(); // bad dragon.age(25); // good dragon.setAge(25);
isVal()
或 hasVal()
// bad if (!dragon.age()) { return false; } // good if (!dragon.hasAge()) { return false; }
function Jedi(options) { options || (options = {}); var lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } Jedi.prototype.set = function(key, val) { this[key] = val; }; Jedi.prototype.get = function(key) { return this[key]; };
// bad $(this).trigger('listingUpdated', listing.id); ... $(this).on('listingUpdated', function(e, listingId) { // do something with listingId });
// good $(this).trigger('listingUpdated', { listingId : listing.id }); ... $(this).on('listingUpdated', function(e, data) { // do something with data.listingId });
$
前缀// bad const sidebar = $('.sidebar'); // good const $sidebar = $('.sidebar');
// bad function setSidebar() { $('.sidebar').hide(); // ...stuff... $('.sidebar').css({ 'background-color': 'pink' }); } // good function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ...stuff... $sidebar.css({ 'background-color': 'pink' }); }
$('.sidebar ul')
或 $('.sidebar ul')
, jsPerffind
// bad $('ul', '.sidebar').hide(); // bad $('.sidebar').find('ul').hide(); // good $('.sidebar ul').hide(); // good $('.sidebar > ul').hide(); // good $sidebar.find('ul').hide();